package org.jvm.instruction.references;

import org.jvm.instruction.base.*;
import org.jvm.rtda.Slots;
import org.jvm.rtda.heap.*;
import org.jvm.rtda.heap.classmember.Field;
import org.jvm.rtda.heap.symref.FieldRef;
import org.jvm.rtda.thread.*;

/**
 * 给类的静态变量赋值
 *
 * @author 海燕
 * @date 2023/2/6
 */
public class PUT_STATIC extends Index16Instruction {


    /**
     * 指令操作数含义为类常量池索引
     *
     * @param frame
     */
    @Override
    public void execute(Frame frame) {
        //从代码执行类常量池获取一个字段符号引用
        Klass thisClass = frame.getMethod().getKlass();
        ConstantPool constantPool = thisClass.getConstantPool();
        FieldRef fieldRef = (FieldRef) constantPool.getConstant(this.index);
        //解析被赋值字段符号引用
        Field field = fieldRef.resolvedField(frame.getThread());
        if (!field.isStatic()) {
            throw new RuntimeException("字段非静态");
        }
        //被赋值字段所在的类
        Klass thatClass = field.getKlass();
        //执行类初始化
        InstructionUtil.initClass(frame.getThread(), thatClass);
        //被final修饰的静态字段，仅可以在字段本身类的默认初始化方法中赋值
        if (field.isFinal()) {
            if (thisClass != thatClass || !frame.getMethod().getName().equals("<clinit>")) {
                throw new RuntimeException("被final修饰的字段，仅可以在字段本身类的默认初始化方法中赋值");
            }
        }
        //给类静态变量根据不同类型进行赋值,从操作数栈中pop变量，赋值给类静态变量
        String descriptor = field.getDescriptor();
        int slotId = field.getSlotId();
        Slots staticVars = thatClass.getStaticVars();
        OperandStack operandStack = frame.getOperandStack();
        switch (descriptor.charAt(0)) {
            case 'Z':
            case 'B':
            case 'C':
            case 'S':
            case 'I':
                staticVars.setInt(slotId, operandStack.popInt());
                break;
            case 'F':
                staticVars.setFloat(slotId, operandStack.popFloat());
                break;
            case 'J':
                staticVars.setLong(slotId, operandStack.popLong());
                break;
            case 'D':
                staticVars.setDouble(slotId, operandStack.popDouble());
                break;
            case 'L':
            case '[':
                staticVars.setRef(slotId, operandStack.popRef());
                break;
            default:
                throw new RuntimeException("unknow type");
        }

    }
}
